1 理论分析
1.1 AHT10介绍
AHT10,新一代温湿度传感器在尺寸与智能方面建立了新的标准:它嵌入了适于回流焊的双列扁平无引脚SMD封装,底面4 x 5mm ,高度1.6mm。传感器输出经过标定的数字信号,标准I2C格式。
AHT10 配有一个全新设计的ASIC专用芯片、一个经过改进的MEMS半导体电容式湿度传感元件和一个标准的片上温度传感元件,其性能已经大大提升甚至超出了前一代传感器的可靠性水平,新一代温湿度传感器,经过改进使其在恶劣环境下的性能更稳定。
1.2 GD32读取 AHT10
AHT10读取流程如下:
1.上电后要等待40ms,读取温湿度值之箭,首先要看状态字的校准使能位Bit[3]是否为1(通过发送0x71可以获取一个字节的状态字),如果不为1,要发送0XEi命令(初始化),此命令参数有两个字节,第一个字节为0x08,第二个字节为0x00。
2.直接发送0xAC命令(触发测量),此命令参数有两个字节,第一个字节为0x33,第二个字节为0x00。
3.等待80ms待测量完成,忙状态Bit[7]为0,然后可以读取六个字节(发0X71即可以读取)。

温度湿度读取时序如下:

注:传感器在采集时需要时间,主机发出测量指令(0xAC)后,延时80毫秒以上再读取转换后的数据并判断返回的状态位是否正常。若状态比特位[Bit7]为0代表数据可正常读取,为1时传感器为忙状态,主机需要等待数据处理完成。
AHT10在睡眠模式下,传感器一直等待主机发送测量命令。CPU此时只执行温湿度测量指令,其余指令无法唤醒传感器进行数据采集。
主机发送采集命令后即可唤醒AHT10进行数据采集,启动采集只需要发送7位从机地址,第八位写0即可。
采集数据时,传感器内部的数字信号处理器会对采集到的温湿度数据进行计算,并且内部算法对数据进行校正运算:采集结束后,传感器的输出寄存器会将数据进行更新,
测量周期由湿度和温度转换后的数字信号处理器(DSP)校正计算。在测量周期结束时,数字电源关闭之前输出寄存器将被更新。测量数据以14位的输出,数据位以右对齐。
AHT10输出的温湿度数据为6个字节,真实的相对湿度(%)和温度(℃)数据通过以下公式进行计算。
相对湿度:

温度转换:

2 实验详解
2.1 实验目的
1) 通过实验掌握GD32 芯片GPIO 的配置方法
2) 掌握温湿度传感器AHT10的原理与使用
2.2 实验设备
硬件:PC 机一台;GD32开发板一套; AHT10模块一个
软件:Windows 10系统,Keil5集成开发环境、串口助手
2.3 实验相关电路图
AHT10模块相关电路如下图所示:

2.4 AHT10数据读取
AHT10 采用标准的 I2C协议进行通讯。
1.启动传感器
将传感器上电,电压为所选择的VDD电源电压(范围介于1.8V与3.6V之间)。上电后,传感器最多需要 20毫秒时间(此时 SCL为高电平)以达到空闲状态,即做好准备接收由主机(MCU)发送的命令。
2.启动/停止时序
每个传输序列都以 Start状态作为开始并以Stop状态作为结束,如下图所示。

启动传输状态(S)-当 SCL 为高电平时,SDA 由高电平转换为低电平。开始状态是由主机控制的一种特殊的总线状态,指示从机传输开始( Start 之后,BUS 总线一般被认为处于占线状态)。

停止传输状态(P)-当SCL 高电平时,SDA 线上从低电平转换为高电平。停止状态是由主机控制的一种特殊的总线状态,指示从机传输结束(Stop 之后,BUS 总线一般被认为处于闲置状态)。
3.发送命令
在启动传输后,随后传输的I2C 首字节包括7位的I2C 设备地址0x38和一个 SDA 方向位(读 R: ‘1’,写 W: ‘0’)。在第 8个 SCL时钟下降沿之后,通过 拉低 SDA 引脚(ACK位),指示传感器数据接收正常。在发出初始化 命令之后(‘1110’0001’代表初始化,‘1010’1100’代表温湿度测量), MCU 必须等待测量完成。

4.软复位
这个命令用于在无需关闭和再次打开电源的情况下,重新启动传感器系统。在接收到这个命令之后,传感器系统开始重新初始化,并恢复默认设置状态,软复位所需时间不
核心代码如下:
/**
* @brief 延时
@param count:要延时的ms数
* @retval None
*/
void aht10_delay(uint32_t count)
{
uint32_t i;
for(i = 0; i < count;i++)
{
delay_us(100);
}
}
/**
* @brief 判断AHT10是否正常
* @param addr: I2C地址
* @retval 1 表示正常, 0 表示不正常
*/
uint8_t aht10_check_ok(uint8_t addr)
{
if (i2c_check_device(addr) == 0)
{
return 1;
}
else
{
/* 失败后,切记发送I2C总线停止信号 */
i2c_stop();
return 0;
}
}
/**
* @brief AHT10初始化
* @param addr: I2C地址
* @return 0,初始化成功,其他,失败
*/
uint8_t aht10_init(uint8_t addr)
{
uint8_t res;
uint8_t temp[2] = {0, 0};
/*-----------------------------------------------------------------------------------*/
i2c_cfg_gpio();
temp[0] = 0x08;
temp[1] = 0x00;
res = aht10_write_data(addr, AHT_CALIBRATION_CMD, temp, 2u);
if(res != 0)
{
return 1;
}
aht10_delay(300);
return res;
}
/**
* @brief 向ATH写入数据
* @param addr:器件IIC地址cmd: 命令
* data: 要写入的数据
* len: 写入数据大小
* @return 0,正常,其他,错误代码
*/
uint8_t aht10_write_data(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len)
{
uint8_t i;
i2c_start();
i2c_send_byte((addr<<1)|0);//发送器件地址+写命令, 从机地址是7位,所以左移一位,末位发送0是写操作命令
if(i2c_wait_ack()) //等待应答 在i2c_wait_ack()中释放总线给从机,从机将SDA拉低 跳过停止位
{
i2c_stop(); //产生一个停止条件
return 1;
}
i2c_send_byte(cmd); //发送命令
i2c_wait_ack(); //等待应答
for(i = 0; i < len; i++)
{
i2c_send_byte(data[i]); //发送数据 一个字节
i2c_wait_ack(); //等待应答
}
i2c_stop(); //产生一个停止条件
return 0;
}
/**
* @brief 读一个字节
* @param addr:器件IIC地址cmd: 命令
* @return 读到的数据
*/
uint8_t aht10_read_one_byte(uint8_t addr)
{
uint8_t res = 0;
i2c_start();
i2c_send_byte((addr << 1) | 0X01); //发送器件地址+读命令
if(i2c_wait_ack()) //等待应答
{
i2c_stop(); //产生一个停止条件
return 1;
}
res = i2c_read_byte();//发送数据
i2c_nack();//读数据,发送nACK
i2c_stop(); //产生一个停止条件
return res;
}
/**
* @brief 读数据
* @param addr:器件IIC地址cmd: 命令
* data: 数据缓存
* len: 读数据大小
* @return 0,正常,其他,错误代码
*/
uint8_t aht10_read_data(uint8_t addr, uint8_t *data, uint8_t len)
{
uint32_t i = 0;
i2c_start();
i2c_send_byte((addr << 1) | 0X01); //发送器件地址+读命令
if(i2c_wait_ack()) //等待应答
{
i2c_stop(); //产生一个停止条件
return 1;
}
for(i = 0; i < len; i++)
{
if(i == (len - 1))
{
data[i] = i2c_read_byte(); //读数据,发送nACK
i2c_nack();
}
else
{
data[i] = i2c_read_byte();
i2c_ack();//读数据,发送ACK
}
}
i2c_stop(); //产生一个停止条件
return 0;
}
/**
* @brief 读取温度数据
* @param addr:器件IIC地址cmd: 命令
* @return 温度数据(单位:摄氏度)
*/
float aht10_read_temperature(uint8_t addr)
{
uint8_t res = 0;
uint8_t cmd[2] = {33, 0};
uint8_t temp[6];
float cur_temp;
res = aht10_write_data(addr, AHT_GET_DATA, cmd, 2); //发送读取数据命令
if(res)
{
return 1;
}
res = aht10_read_data(addr, temp, 6); //读取数据
if(res)
{
return 1;
}
cur_temp = ((temp[3] & 0xf) << 16 | temp[4] << 8 | temp[5]) * 200.0 / (1 << 20) - 50;
return cur_temp;
}
/**
* @brief 读取湿度数据
* @param addr:器件IIC地址cmd: 命令
* @return 湿度数据(单位:%RH)
*/
float aht10_read_humidity(uint8_t addr)
{
uint8_t res = 0;
uint8_t cmd[2] = {33, 0};
uint8_t humi[6];
float cur_humi;
res = aht10_write_data(addr, AHT_GET_DATA, cmd, 2); //发送读取数据命令
if(res)
{
return 1;
}
res = aht10_read_data(addr, humi, 6); //读取数据
if(res)
{
return 1;
}
cur_humi = ((humi[1]) << 12 | humi[2] << 4 | (humi[3] & 0xF0)) * 100.0 / (1 << 20);
return cur_humi;
}
主函数代码如下。
/*
brief main function
param[in] none
param[out] none
retval none
*/
int main(void)
{
st_aht10_data aht10_data;
st_bsp_usart_dev bsp_usart_dev0 = USART_DEV0_CONFIG;
st_bsp_led_dev bsp_led_dev0 = LED_DEV0_CONFIG;
//systick init
sysTick_init();
// LED1 init
bsp_led_init(&bsp_led_dev0);
//usart init 115200 8-N-1
bsp_usart_init(&bsp_usart_dev0, USART_MODE_EXTI, 115200, 0, 1);
// DMA config
bsp_usart_dma_init();
/* USART DMA 发送使能 */
usart_dma_enable(USART0, USART_DMA_TRANSMIT);
/* USART DMA接收使能 */
usart_dma_enable(USART0, USART_DMA_RECEIVE);
aht10_init(AHT_IIC_ADDR);
while(1)
{
aht10_data.temp = aht10_read_temperature(AHT_IIC_ADDR);
aht10_data.hum = aht10_read_humidity(AHT_IIC_ADDR);
printf("temp = %.2f°C, hum = %.2f%%\r\n", aht10_data.temp, aht10_data.hum);
bsp_led_toggle(&bsp_led_dev0);
delay_ms(1000);
}
}
2.5 实验现象
接上AHT10设备后,每隔1s就输出温湿度。

欢迎访问我的网站
BruceOu的哔哩哔哩
BruceOu的主页
BruceOu的博客
BruceOu的CSDN博客
BruceOu的简书
BruceOu的知乎
欢迎订阅我的微信公众号
关注公众号[嵌入式实验楼]获取更多资讯
欢迎订阅我的知识星球
关注知识星球[嵌入式实验楼]获取更多资讯
资源获取方式
1.关注公众号[嵌入式实验楼]
2.在公众号回复关键词[GD32开发实战指南]获取资料提取码